#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include "prj1u.h"

static dev_t g_devno;
static struct cdev g_chrdev;
struct class *g_class;
struct device *g_device;
struct device_node    *g_dtsNode;
int g_pinNo;

static int BuzCdevOpen(struct inode *inode, struct file *filp)
{
    printk("Buz Cdev Opened \n");
    return 0;
}

static ssize_t BuzCdevWrite(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    unsigned char write_data;
    int error = copy_from_user(&write_data, buf, cnt);
    if (error < 0) {
        printk("buz.ko write failed \n");
        return -1;
    }
    gpio_direction_output(g_pinNo, write_data ? 1 : 0);

    return 0;
}

static struct file_operations  g_buzCdevFops = 
{
    .owner = THIS_MODULE,
    .open = BuzCdevOpen,
    .write = BuzCdevWrite,
};

static int BuzProbe(struct platform_device *pdv)
{
    int ret = 0;
    printk("[%s] match successed\n", DRVNAME_MAINBD_BUZ);

    g_dtsNode = of_find_node_by_path(DRVPATH_MAINBD_BUZ);
    if (g_dtsNode == NULL) {
        printk(KERN_EMERG "get buz.ko[%s] failed!", DRVPATH_MAINBD_BUZ);
    }

    g_pinNo = of_get_named_gpio(g_dtsNode, "gpios", 0);
    printk("[%s] g_pinNo = %d \n", DRVNAME_MAINBD_BUZ, g_pinNo);
    gpio_direction_output(g_pinNo, 0);

    // 采用动态分配的方式，获取设备编号，次设备号为0，
    // DEV_CNT为1，当前只申请一个设备编号
    ret = alloc_chrdev_region(&g_devno, 0, DEV_CNT_1, DRVNAME_MAINBD_BUZ);
    if (ret < 0) {
        printk("[%s] fail to alloc g_devno\n", DRVNAME_MAINBD_BUZ);
        goto alloc_err;
    }

    // 关联字符设备结构体cdev与文件操作结构体file_operations
    g_chrdev.owner = THIS_MODULE;
    cdev_init(&g_chrdev, &g_buzCdevFops);

    // 添加设备至cdev_map散列表中
    ret = cdev_add(&g_chrdev, g_devno, DEV_CNT_1);
    if (ret < 0) {
        printk("[%s] fail to add cdev\n", DRVNAME_MAINBD_BUZ);
        goto add_err;
    }

    // 创建类
    g_class = class_create(THIS_MODULE, DRVNAME_MAINBD_BUZ);
    g_device = device_create(g_class, NULL, g_devno, NULL, DRVNAME_MAINBD_BUZ);

    return 0;

add_err:
    //添加设备失败时，需要注销设备号
    unregister_chrdev_region(g_devno, DEV_CNT_1);
    printk("[%s] add_err\n", DRVNAME_MAINBD_BUZ);
alloc_err:
    return -1;
}

static const struct of_device_id g_mainbdBuzID[] = {
    { .compatible = "fire,mainbd_buz"},
    { /* sentinel */ }
};

struct platform_driver g_mainbdBuzPlatformDrv = {
    .probe = BuzProbe,
    .driver = {
        .name = DRVNAME_MAINBD_BUZ,
        .owner = THIS_MODULE,
        .of_match_table = g_mainbdBuzID,
    }
};

static int __init BuzPlatformDrvInit(void)
{
    int DriverState = platform_driver_register(&g_mainbdBuzPlatformDrv);

    printk(KERN_EMERG "[%s] DriverState: %d\n", DRVNAME_MAINBD_BUZ, DriverState);
    return 0;
}

static void __exit BuzPlatformDrvExit(void)
{
    printk(KERN_EMERG "[%s] Exit\n", DRVNAME_MAINBD_BUZ);
    device_destroy(g_class, g_devno);
    class_destroy(g_class);
    cdev_del(&g_chrdev);
    unregister_chrdev_region(g_devno, DEV_CNT_1);
    platform_driver_unregister(&g_mainbdBuzPlatformDrv);    
}


module_init(BuzPlatformDrvInit);
module_exit(BuzPlatformDrvExit);

MODULE_LICENSE("GPL");

